package cave.layer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import android.util.Log;
import cave.LayerFactory;
import cave.game.ActivityEvent;
import cave.game.GameServices;
import cave.game.InputEvent;
import cave.game.MainActivity;
import cave.game.SharedPreferences;
import cave.renderer.RenderList;

public class LayerManager {
	
	private LayerFactory factory;
	private HashMap<String, Layer> layers;
	private HashSet<String> createdLayers;
	private ArrayList<Layer> openLayers;
	private ArrayList<Layer> activeLayers;
	private ArrayList<Layer> softPausedLayers;
	private HashSet<String> pausedLayers;
	private HashSet<String> closedLayers;
	private ArrayList<LayerEvent> layerEvents;
	private SharedPreferences sharedPrefs;
	
	
	public LayerManager(GameServices gameServices) {
		sharedPrefs = gameServices.getSharedPrefs();
		factory = new LayerFactory(this, gameServices);
		layers = new HashMap<String, Layer>();
		createdLayers = new HashSet<String>();
		openLayers = new ArrayList<Layer>();
		activeLayers = new ArrayList<Layer>();
		softPausedLayers = new ArrayList<Layer>();
		pausedLayers = new HashSet<String>();
		closedLayers = new HashSet<String>();
		layerEvents = new ArrayList<LayerEvent>();
		
		sharedPrefs.loadPreferences();
		
		String[] startLayers = factory.getStartLayerIDs();
		for (int i = 0; i < startLayers.length; i++) {
			openLayer(startLayers[i]);
		}
	}
	
	public boolean update(float dt, RenderList renderList,
			ArrayList<InputEvent> inputEvents, ArrayList<ActivityEvent> activityEvents) {
		
		boolean needToSave = false;
		
		// Handle activity events
		for (int i = 0; i < activityEvents.size(); i++) {
			if (activityEvents.get(i) == ActivityEvent.PAUSE) {
				for (int ii = 0; ii < activeLayers.size(); ii++) {
					softPausedLayers.add(activeLayers.get(ii));
					pauseLayer(activeLayers.get(ii).getId());
				}
				needToSave = true;
			} else { // activityEvent is RESUME
				for (int ii = 0; ii < softPausedLayers.size(); ii++) {
					resumeLayer(softPausedLayers.get(ii).getId());
				}
				softPausedLayers.clear();
			}
		}
		
		// Handle layer events
		for (int i = 0; i < layerEvents.size(); i++) {
			
			String layerId = layerEvents.get(i).getLayerId();
			switch (layerEvents.get(i).getType()) {
			
				case CREATE:
					if (!createdLayers.contains(layerId)) {
						if (!layers.containsKey(layerId)) {
							layers.put(layerId, factory.createLayer(layerId));
						}
						createdLayers.add(layerId);
						layers.get(layerId).onCreate();
					} else {
						Log.w(MainActivity.TAG, "LayerManager.update: layer "+layerId+" is already created");
					}
					break;
					
				case OPEN:
					if (!openLayers.contains(layers.get(layerId))) {
						if (!createdLayers.contains(layerId)) {
							if (!layers.containsKey(layerId)) {
								layers.put(layerId, factory.createLayer(layerId));
							}
							createdLayers.add(layerId);
							layers.get(layerId).onCreate();
						}
						openLayers.add(layers.get(layerId));
						activeLayers.add(layers.get(layerId));
						closedLayers.remove(layerId);
						layers.get(layerId).onOpen();
					} else {
						Log.w(MainActivity.TAG, "LayerManager.update: layer "+layerId+" is already open");
					}
					break;
					
				case PAUSE:
					if (!pausedLayers.contains(layerId)) {
						if (!openLayers.contains(layers.get(layerId))) {
							if (!createdLayers.contains(layerId)) {
								if (layers.containsKey(layerId)) {
									layers.put(layerId, factory.createLayer(layerId));
								}
								createdLayers.add(layerId);
								layers.get(layerId).onCreate();
							}
							openLayers.add(layers.get(layerId));
							activeLayers.add(layers.get(layerId));
							pausedLayers.remove(layerId);
							closedLayers.remove(layerId);
							layers.get(layerId).onOpen();
						}
						pausedLayers.add(layerId);
						activeLayers.remove(layers.get(layerId));
						layers.get(layerId).onPause();
					} else {
						Log.w(MainActivity.TAG, "LayerManager.update: layer "+layerId+" is already on pause");
					}
					break;
					
				case RESUME:
					if (!activeLayers.contains(layers.get(layerId))) {
						if (!openLayers.contains(layers.get(layerId))) {
							if (!createdLayers.contains(layerId)) {
								createdLayers.add(layerId);
								layers.get(layerId).onCreate();
							}
							openLayers.add(layers.get(layerId));
							activeLayers.add(layers.get(layerId));
							pausedLayers.remove(layerId);
							closedLayers.remove(layerId);
							layers.get(layerId).onOpen();
						}
						activeLayers.add(layers.get(layerId));
						pausedLayers.remove(layerId);
						layers.get(layerId).onResume();
					} else {
						Log.w(MainActivity.TAG, "LayerManager.update: layer "+layerId+" is already active");
					}
					break;
					
				case CLOSE:
					if (!closedLayers.contains(layerId)) {
						if (!openLayers.contains(layers.get(layerId))) {
							if (!createdLayers.contains(layerId)) {
								if (layers.containsKey(layerId)) {
									layers.put(layerId, factory.createLayer(layerId));
								}
								createdLayers.add(layerId);
								layers.get(layerId).onCreate();
							}
							openLayers.add(layers.get(layerId));
							activeLayers.add(layers.get(layerId));
							pausedLayers.remove(layerId);
							closedLayers.remove(layerId);
							layers.get(layerId).onOpen();
						}
						closedLayers.add(layerId);
						openLayers.remove(layers.get(layerId));
						activeLayers.remove(layers.get(layerId));
						pausedLayers.remove(layerId);
						layers.get(layerId).onClose();
					} else {
						Log.w(MainActivity.TAG, "LayerManager.update: layer "+layerId+" is already closed");
					}
					break;
					
				case DESTROY:
					if (!closedLayers.contains(layerId)) {
						if (!openLayers.contains(layers.get(layerId))) {
							if (!createdLayers.contains(layerId)) {
								if (layers.containsKey(layerId)) {
									layers.put(layerId, factory.createLayer(layerId));
								}
								createdLayers.add(layerId);
								layers.get(layerId).onCreate();
							}
							openLayers.add(layers.get(layerId));
							activeLayers.add(layers.get(layerId));
							pausedLayers.remove(layerId);
							closedLayers.remove(layerId);
							layers.get(layerId).onOpen();
						}
						closedLayers.add(layerId);
						openLayers.remove(layers.get(layerId));
						activeLayers.remove(layers.get(layerId));
						pausedLayers.remove(layerId);
						layers.get(layerId).onClose();
					}
					createdLayers.remove(layerId);
					layers.get(layerId).onDestroy();
					break;
				default:
				Log.w(MainActivity.TAG, "LayerManager.update: unknown InputEvent");
			}
		}
		layerEvents.clear();
		
		// Save shared preferences if activity is paused
		if (needToSave) {
			sharedPrefs.savePreferences();
		}
		
		// Update layers
		if (openLayers.size() != 0) {
			for (int i = 0; i < activeLayers.size(); i++) {
				activeLayers.get(i).onUpdate(dt, inputEvents);
			}
		} else {
			// No open layers
			if (createdLayers.size() > 0) {
				Iterator<String> i = createdLayers.iterator();
				while(i.hasNext()) {
					destroyLayer(i.next());
				}
			} else {
				return false;
			}
		}
		
		// Draw layers
		for (int i = 0; i < openLayers.size(); i++) {
			openLayers.get(i).draw(renderList);
		}
		
		return true;
	}
	
	public void createLayer(String layerId) {
		layerEvents.add(new LayerEvent(layerId, LayerEvent.Type.CREATE));
	}

	public void openLayer(String layerId) {
		layerEvents.add(new LayerEvent(layerId, LayerEvent.Type.OPEN));
	}
	
	public void pauseLayer(String layerId) {
		layerEvents.add(new LayerEvent(layerId, LayerEvent.Type.PAUSE));
	}
	
	public void resumeLayer(String layerId) {
		layerEvents.add(new LayerEvent(layerId, LayerEvent.Type.RESUME));
	}

	public void closeLayer(String layerId) {
		layerEvents.add(new LayerEvent(layerId, LayerEvent.Type.CLOSE));
	}
	
	public void destroyLayer(String layerId) {
		layerEvents.add(new LayerEvent(layerId, LayerEvent.Type.DESTROY));
	}
	
	public void pauseActiveLayers() {
		for (int i = 0; i < activeLayers.size(); i++) {
			layerEvents.add(new LayerEvent(activeLayers.get(i).getId(),
					LayerEvent.Type.PAUSE));
		}
	}
	
	public void resumePausedLayers() {
		Iterator<String> i = pausedLayers.iterator();
		while(i.hasNext()) {
			layerEvents.add(new LayerEvent(i.next(), LayerEvent.Type.RESUME));
		}
	}
	
	public void closeOpenLayers() {
		for (int i = 0; i < openLayers.size(); i++) {
			layerEvents.add(new LayerEvent(openLayers.get(i).getId(), LayerEvent.Type.CLOSE));
			
		}
	}
	
	public void destroyCreatedLayers() {
		Iterator<String> i = createdLayers.iterator();
		while(i.hasNext()) {
			layerEvents.add(new LayerEvent(i.next(), LayerEvent.Type.DESTROY));
		}
	}
	
	
	public boolean isLayerCreated(String layerId) {
		if (createdLayers.contains(layerId)) {
			return true;
		} else {
			return false;
		}
	}
	
	public boolean isLayerOpen(String layerId) {
		if (openLayers.contains(layerId)) {
			return true;
		} else {
			return false;
		}
	}
	
	public boolean isLayerPaused(String layerId) {
		if (pausedLayers.contains(layerId)) {
			return true;
		} else {
			return false;
		}
	}
	
	public Layer getLayer(String layerId) {
		if (layers.containsKey(layerId)) {
			return layers.get(layerId);
		} else {
			layers.put(layerId, factory.createLayer(layerId));
			return layers.get(layerId);
		}
	}

}
